package com.dongnao.network.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

/**
 * 群聊系统
 * 服务器端可以监测用户上线、下线、转发消息
 * 客户端可以收发消息
 */
public class GroupChatServer {

    //选择器和服务器通道
    Selector selector;
    static final int port = 8899;

    //构造器进行初始化
    public GroupChatServer() {
        try {
            selector = Selector.open();
            ServerSocketChannel channel = ServerSocketChannel.open();
            //设置为非阻塞
            channel.configureBlocking(false);
            //绑定端口
            channel.bind(new InetSocketAddress(port));
            //服务器通道注册到选择器，并关注连接事件
            channel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("服务器启动成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void listen() {
        try {
            while (true) {
                int num = selector.select();
                if (num > 0) {//有事件处理
                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()) {
                        SelectionKey key = iterator.next();
                        iterator.remove();

                        if (key.isAcceptable()) {//连接事件
                            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                            SocketChannel sc = ssc.accept();
                            sc.configureBlocking(false);
                            //将这个与客户端之间的通道注册到select
                            sc.register(selector, SelectionKey.OP_READ);

                            System.out.println(sc.getRemoteAddress() + "上线");
                        } else if (key.isReadable()) {
                            readData(key);
                        }
                    }
                } else {
                    System.out.println("等待...");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void readData(SelectionKey key) {
        SocketChannel socketChannel = null;
        try {
            socketChannel = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);

            int read = socketChannel.read(buffer);

            if (read > 0) {
                String s = new String(buffer.array());
                System.out.println("收到客户端消息：[ " + s + " ]");

                //向其他客户端发送这个消息
                sendMessageToOtherClient(s, socketChannel);
            }
        } catch (Exception e) {
            try {
                System.out.println(socketChannel.getRemoteAddress() + "离线了");
                key.cancel();//取消这个键对应的通道的注册
                socketChannel.close();
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
            e.printStackTrace();
        }
    }

    private void sendMessageToOtherClient(String s, SocketChannel socketChannel) {
        System.out.println("给客户端转发消息");
        for (SelectionKey key : selector.keys()) {
            SelectableChannel ch = key.channel();
            if (ch instanceof SocketChannel && ch != socketChannel) {
                SocketChannel channel = (SocketChannel) ch;
                try {
                    ByteBuffer buffer = ByteBuffer.wrap(s.getBytes());
                    channel.write(buffer);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        GroupChatServer server = new GroupChatServer();
        server.listen();
    }
}
